﻿using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System.IO;

namespace Assets.Scripts.LDS
{

	// Translate: Finished
	// Debug: 

	class RelativePos
	{

		public RelativePos() { }

		public Vector3 pos;  // rel pos of actObj in anchor's unit frame
		public double theta; // normalized, from -pi to pi
		public Matrix4x4 anchorAlignMat=new Matrix4x4();  // transformation of anchorObj to unit frame
		public Matrix4x4 actAlignMat=new Matrix4x4();  // transformation of actObj to anchorObj's unit frame,  anchorAlignMat*actInitTransMat
		public double unitFactor;

		public string m_actObjName;
		public string m_anchorObjName;
		public string m_conditionName;
		public string m_instanceNameHash;  // anchorObjName_actObjName_conditionName


		public int m_actObjId;
		public int m_anchorObjId;
		public string m_sceneName;
		public string m_instanceIdHash;  // sceneName_anchorObjId_actObjId


		bool isValid;
	}
	class PairwiseRelationModel
	{
		public PairwiseRelationModel(string anchorName, string actName, string conditionName, string relationName = "general")
		{
			m_anchorObjName = anchorName;
			m_actObjName = actName;
			m_conditionName = conditionName;
			m_relationName = relationName;
			m_relationKey = m_anchorObjName + "_" + m_actObjName + "_" + m_conditionName + "_" + m_relationName;
			m_GMM = null;

			m_lastSampleInstanceId = -1;
		}
		public void loadFromStream(StreamReader sr)
		{
			string currLine;

			// gaussNum instanceNum
			currLine = sr.ReadLine();
			List<float> floatList = Util.StringToFloatList(currLine, "");
			m_numGauss = (int)floatList[0];
			m_numInstance = (int)floatList[1];

			List<float> probTh = new List<float>();
			for (int i = 0; i < floatList.Count - 2; i++)
			{
				probTh.Add(floatList[i + 2]);
			}

			List<string> parts;

			if (m_numGauss == 0)
			{
				currLine = sr.ReadLine();
				parts = Util.PartitionString(currLine, ",");

				m_instances.Clear();
				m_candidateInstanceIds.Clear();

				for (int t = 0; t < m_numInstance; t++)
				{
					// observations
					floatList = Util.StringToFloatList(parts[t], "");
					RelativePos newRelPos = new RelativePos();
					newRelPos.pos = new Vector3(floatList[0], floatList[1], floatList[2]);
					newRelPos.theta = floatList[3];
					newRelPos.m_anchorObjName = m_anchorObjName;
					newRelPos.m_actObjName = m_actObjName;
					newRelPos.m_conditionName = m_conditionName;

					m_instances.Add(newRelPos);
					m_candidateInstanceIds.Add(t);
				}
			}
			else
			{
				m_GMM = new GaussianMixtureModel(m_numGauss);
				m_GMM.m_probTh = probTh;

				for (int g = 0; g < m_numGauss; g++)
				{
					currLine = sr.ReadLine();

					parts = Util.PartitionString(currLine, ",");
					int gaussDim = int.Parse(parts[0]);
					double gaussWeight = float.Parse(parts[1]);

					List<float> mean = new List<float>();
					List<List<float>> covarMat = new List<List<float>>();
					floatList = Util.StringToFloatList(parts[2], "");

					for (int d = 0; d < gaussDim; d++)
					{
						mean.Add(floatList[d]);   // have to set the size before you can use the comma initializer.
					}

					floatList = Util.StringToFloatList(parts[3], "");
					for (int r = 0; r < gaussDim; r++)
					{
						covarMat.Add(new List<float>());
						for (int c = 0; c < gaussDim; c++)
						{
							covarMat[r].Add(floatList[c * gaussDim + r]);
						}
					}

					GaussianModel newGauss = new GaussianModel(gaussDim, gaussWeight, ref mean, ref covarMat);
					m_GMM.m_gaussians.Add(newGauss);
				}
			}
		}
		public int m_numGauss;
		public GaussianMixtureModel m_GMM;

		public int m_numInstance;
		public List<RelativePos> m_instances=new List<RelativePos>();

		public string m_relationKey;  // anchorName_actName_conditionName_relationName

		public string m_actObjName;
		public string m_anchorObjName;

		public string m_conditionName; // parent-child, sibling, proximity, or text-specified relation
		public string m_relationName;  // none, left, right, etc.

		public List<int> m_candidateInstanceIds=new List<int>();
		public int m_lastSampleInstanceId;

		public int m_modelId;
		public List<int> m_simModelIds=new List<int>(); // list of similar relation model ids, sorted from high sim to low

		public List<List<float>> m_avgObjFeatures=new List<List<float>>();  // heightToFloor, modelHeight, modelVolume of anchor and act obj
		public List<List<float>> m_maxObjFeatures=new List<List<float>>();
	}
	class OccurrenceModel
	{
		public OccurrenceModel(string objName, ref int objNum)
		{
			m_objName = objName;
			m_objNum = objNum;
			m_occurKey = string.Format("{0}_{1}", m_objName, m_objNum);
			m_numInstance = 0;
			m_occurProb = 0;
		}

		public int m_objNum;
		public int m_numInstance;   // number of observations
		public double m_occurProb;

		public string m_objName; // active object name
								 //QString m_conditionName; // condition to the anchor object

		public string m_occurKey; // objName_objNum
	}
	class CoOccurrenceModel
	{
		public CoOccurrenceModel()
		{
			m_firstObjNum = 0;
			m_secondObjNum = 0;
			m_coOccNum = 0;
			m_prob = 0;
		}
		public CoOccurrenceModel(string firstObjName, string secondObjName, string anchorObjName, string conditionName)
		{
			m_firstObjName = firstObjName;
			m_secondObjName = secondObjName;
			m_conditionName = conditionName;
			m_anchorObjName = anchorObjName;
			m_coOccurKey = m_firstObjName + "_" + m_secondObjName + "_" + m_conditionName + "_" + m_anchorObjName;

			m_firstObjNum = 0;
			m_secondObjNum = 0;
			m_coOccNum = 0;
			m_prob = 0;
		}
		public void loadFromStream(StreamReader sr)
		{
			string currLine;

			currLine = sr.ReadLine();
			List<string> parts = Util.PartitionString(currLine, ",");
			m_coOccurKey = parts[0];
			m_prob = float.Parse(parts[1]);

			List<string> subParts = Util.PartitionString(parts[0], "_");
			m_firstObjName = subParts[0];
			m_secondObjName = subParts[1];
			m_conditionName = subParts[2];
			m_anchorObjName = subParts[3];

			currLine = sr.ReadLine();
			List<int> intList = Util.StringToIntegerList(currLine, "", ",");
			m_coOccNum = intList[0];
			m_firstObjNum = intList[1];
			m_secondObjNum = intList[2];
		}

		public int m_firstObjNum;  // observation of first Obj in a group or on an anchorObj
		public int m_secondObjNum; // 
		public int m_coOccNum;

		public double m_prob;

		public string m_firstObjName;
		public string m_secondObjName;
		public string m_anchorObjName;

		public string m_conditionName;  // sibling, or groupName, e.g., messy
		public string m_coOccurKey; // firstObjName_secondObjName_conditionName_anchorName
	}
	class GroupRelationModel
	{
		public GroupRelationModel(string anchorObjName, string relationName)
		{
			m_anchorObjName = anchorObjName;
			m_relationName = relationName;
			m_groupKey = m_relationName + "_" + m_anchorObjName;
			m_numInstance = 0;
			m_maxOccProb = 0;
		}

		public void computeMaxOccProbs()
		{
			m_maxOccProb = 0;

			foreach (var iter in m_occurModels)
			{
				OccurrenceModel occModel = iter.Value;
				if (occModel.m_occurProb > m_maxOccProb)
				{
					m_maxOccProb = occModel.m_occurProb;
				}
			}
		}
		public Dictionary<string, PairwiseRelationModel> m_pairwiseModels=new Dictionary<string, PairwiseRelationModel>();  // relation-conditioned relative model
		public List<string> m_pairwiseModelKeys=new List<string>();
		public Dictionary<string, OccurrenceModel> m_occurModels=new Dictionary<string, OccurrenceModel>();

		public string m_anchorObjName;
		public string m_relationName;

		public string m_groupKey;  // relationName_anchorObjName
		public int m_numInstance;

		public double m_maxOccProb;

	}
	class SupportRelation
	{
		public SupportRelation()
		{
			m_childProbGivenParent = 0;
			m_parentProbGivenChild = 0;
		}
		public SupportRelation(string parentName, string childName, string supportType)
		{
			m_suppRelKey = m_parentName + "_" + m_childName + "_" + m_supportType;
			m_childProbGivenParent = 0;
			m_parentProbGivenChild = 0;
		}

		public void loadFromStream(StreamReader sr)
		{
			string currLine;
			currLine = sr.ReadLine();
			List<string> parts = Util.PartitionString(currLine, "_");

			m_parentName = parts[0];
			m_childName = parts[1];
			m_supportType = parts[2];
			m_suppRelKey = m_parentName + "_" + m_childName + "_" + m_supportType;

			currLine = sr.ReadLine();
			List<float> floatList = Util.StringToFloatList(currLine, "");
			m_childProbGivenParent = (double)floatList[0];
			m_parentProbGivenChild = (double)floatList[1];
		}

		public double m_childProbGivenParent;
		public double m_parentProbGivenChild;

		public string m_parentName;
		public string m_childName;
		public string m_supportType;  // vertsupport, horizonsupport

		public string m_suppRelKey;


	}
	class GaussianModel
	{
		public GaussianModel(int d, double w, ref List<float> m, ref List<List<float>> c)
		{
			dim = d;
			weight = w;
			mean = m;
			covarMat = c;
			m_sampler = new Eigen.EigenMultivariateNormal(mean, covarMat);
		}

		public int dim;
		public double weight;   // mixing weight
		public List<float> mean = new List<float>();
		public List<List<float>> covarMat = new List<List<float>>();

		private Eigen.EigenMultivariateNormal m_sampler;
	}
	class GaussianMixtureModel
	{
		public GaussianMixtureModel(int n)
		{
			for (int i = 0; i < m_numGauss; i++)
			{
				m_gaussians.RemoveAt(i);
			}

			m_gaussians.Clear();
		}

		public List<GaussianModel> m_gaussians=new List<GaussianModel>();

		// probability value that X percentiles of observations have passed, currently X = [20 50 80]
		public List<float> m_probTh = new List<float>();
		public int m_numGauss;

	}
}